package com.elasticcloudservice.predict;

import com.filetool.util.MyUtil;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class Predict {

    public static final int FLAVOR_TYPES = 18;
    public static final int ITERATORS = 2600;  //梯度下降最大迭代次数
    public static final double MAX_COST = 0.00000000000000000001; //最大允许的损失函数值
    public static final double LEARNING_RATE = 0.01; //学习率
    public static final double K = 3.5;

//    public static final double[] factor = {1.5, 0.7, 2.0, 1.7, 0.0, 0.0, 1.7, 2.3, 2.0, 1.5, 1.7, 1.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; //L2case1, case2参数
//    public static final double[] factor = {1.6, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.3, 0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; //L2case2参数
//    public static final double[] factor = {1.5, 0.7, 2.0, 1.7, 1.7, 1.7, 1.7, 2.3, 2.0, 1.5, 1.7, 1.3, 1.7, 1.7, 1.7, 1.7, 0.7, 0.7};  //所有的参数

    public static String[] predictVm(String[] ecsContent, String[] inputContent) throws ParseException {

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //训练时间跨度
        String firstTrainDate = (ecsContent[0].split("\t"))[2];
        int count = 1;
        for (int i = 1; i < ecsContent.length; i++) {
            if (ecsContent[i].contains("\t") && ecsContent[i].split("\t").length == 3) {
                count++;
            }
        }
        String lastTrainDate = (ecsContent[count - 1].split("\t"))[2];
        Long trainStart = df.parse(firstTrainDate).getTime();
        Long trainEnd = df.parse(lastTrainDate).getTime();
        int trainDays = (int) Math.ceil((trainEnd - trainStart) * 1.0 / 86400000);
        //预测时间跨度
        int serverType = Integer.parseInt(inputContent[0]);
        int flavorType = Integer.parseInt(inputContent[2 + serverType]);  //虚拟机种类
        String firstPredictDate = inputContent[serverType + flavorType + 4];
        String lastPredictDate = inputContent[serverType + flavorType + 5];
        Long predictStart = df.parse(firstPredictDate).getTime();
        Long predictEnd = df.parse(lastPredictDate).getTime();
        int predictDays = (int) Math.ceil((predictEnd - predictStart) * 1.0 / 86400000);

        //预测开始时间与训练数据结束时间的间隔
        int interval = (int) Math.floor((predictStart - trainEnd) * 1.0 / 86400000);

        //获取输入文件中的虚拟机信息
        List<String> nameListFromInput = getNameListFromInput(inputContent);
        String[] predict = new String[nameListFromInput.size()];

        //各种虚拟机每天出现的数量
        int[][] flavorNumByDay = MyUtil.getFlavorNumByDay(ecsContent);

        //将空缺数据补上
//        flavorNumByDay = patchData(trainDays, interval, flavorNumByDay);

        if (interval != 0) {
//            if (interval < predictDays)
//                interval = 10;
            if (interval < predictDays)
                interval = (int)Math.ceil(predictDays * 1.0 / 1.4);
            //统计各虚拟机在每个窗口中的数量
            int[] interValPredictResult;
            int[][] intervalFlavorNumInWindow = getDataInWindow(trainDays, interval, flavorNumByDay);
            //各种虚拟机出现次数的累加
            intervalFlavorNumInWindow = MyUtil.getSumOfFlavor(intervalFlavorNumInWindow);
            //训练模型
            double[][] weights = trainModel(intervalFlavorNumInWindow);
            //以间隔的天数滑窗，预测间隔期的结果
            interValPredictResult = predictResult(intervalFlavorNumInWindow, weights);
            int i = 0;
            for (String vmName : nameListFromInput) {
                int index = Integer.parseInt(vmName.substring(6)) - 1;  //要预测的flavor类别，比如flavor7，则index = 6
                int temp = interValPredictResult[index];
//
//                if (index == 0 || index == 9)
//                    temp = (int) Math.floor(temp * 1.5 / interval * predictDays);
//                else if (index == 1)
//                    temp = (int) Math.floor(temp * 0.7 / interval * predictDays);
//                else if (index == 3 || index == 6 || index == 10)
//                    temp = (int) Math.floor(temp * 1.7 / interval * predictDays);
//                else if (index == 11)
//                    temp = (int) Math.floor(temp * 1.3 / interval * predictDays);

//                if (index == 2 || index == 7 || index == 8)  //84.356
//                    temp = (int) Math.floor(temp * 2.0 / interval * predictDays);
//                else
//                    temp = (int) Math.floor(temp * 1.6 / interval * predictDays);

                if (index == 7 || index == 8)
                    temp = (int) Math.floor(temp * 2.1 / interval * predictDays);
                else
                    temp = (int) Math.floor(temp * 1.4 / interval * predictDays);

//                temp = (int) Math.floor(temp * factor[index] / interval * predictDays);

                if (temp < 0)
                    temp = 0;
                predict[i++] = vmName + " " + temp;
            }

        } else {

            //对数据做平滑处理
            smooth(flavorNumByDay);
//        smoothByBox(flavorNumByDay); //箱形图去噪

            //统计各虚拟机在每个窗口中的数量
            int[][] flavorNumInWindow = getDataInWindow(trainDays, predictDays, flavorNumByDay);

            //各种虚拟机出现次数的累加
            flavorNumInWindow = MyUtil.getSumOfFlavor(flavorNumInWindow);

            //训练模型
            double[][] weights = trainModel(flavorNumInWindow);

            //预测结果
            int[] predictResult = predictResult(flavorNumInWindow, weights);

            int i = 0;
            for (String vmName : nameListFromInput) {
                int result = (int)Math.ceil(predictResult[Integer.parseInt(vmName.substring(6)) - 1]);
                predict[i++] = vmName + " " + result;
            }
        }
        //根据预测结果，分配虚拟机到服务器
        return Allocate.allocate(inputContent, predict);
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 预测断续时间内的数据
     * @Date: 13:54 2018/4/20
     */
    private static int[][] patchData(int trainDays, int interval, int[][] flavorNumByDay) {
        if (interval != 0) {
            //统计各虚拟机在每个窗口中的数量
            int[][] intervalFlavorNumInWindow = getDataInWindow(trainDays, interval, flavorNumByDay);
            //各种虚拟机出现次数的累加
            intervalFlavorNumInWindow = MyUtil.getSumOfFlavor(intervalFlavorNumInWindow);
            //训练模型
            double[][] weights = trainModel(intervalFlavorNumInWindow);
            //以间隔的天数滑窗，预测间隔期的结果
            int[] interValPredictResult = predictResult(intervalFlavorNumInWindow, weights);

            int k = 0;
            while (k++ < interval) {
                int[][] newFlavorNumByDay = new int[flavorNumByDay.length + 1][FLAVOR_TYPES];
                for (int i = 0; i < flavorNumByDay.length; i++) {
                    for (int j = 0; j < flavorNumByDay[i].length; j++) {
                        newFlavorNumByDay[i][j] = flavorNumByDay[i][j];
                    }
                }
                for (int i = 0; i < FLAVOR_TYPES; i++) {
                    newFlavorNumByDay[newFlavorNumByDay.length - 1][i] = interValPredictResult[i] / interval;
                }
                flavorNumByDay = newFlavorNumByDay;
            }

//            int i = 0;
//            while (i++ < interval){
//                int[][] flavorNumInWindow = MyUtil.getSumOfFlavor(flavorNumByDay);   //各种虚拟机出现次数的累加
//                flavorNumByDay = predictNextDay(flavorNumInWindow, flavorNumByDay);  //补上下一天的数据 线性回归
////                flavorNumByDay = predictResultByTwoSmooth(flavorNumInWindow, flavorNumByDay); //补上下一天的数据 指数平滑
//            }
        }
        return flavorNumByDay;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 二次指数平滑预测
     * @Date: 16:28 2018/3/17
     */
    private static int[][] predictResultByTwoSmooth(int[][] flavorNumInWindow, int[][] flavorNumByDay) {

        int[] predictResult = new int[FLAVOR_TYPES];
        double factor = 0.9;
        for (int i = 0; i < FLAVOR_TYPES; i++) {

            int[] flavorData = new int[flavorNumInWindow.length]; //flavor的历史数据
            double initialSmoothVal = 0.0; //平滑系数初始值
            for (int j = 0; j < flavorNumInWindow.length; j++) {
                flavorData[j] = flavorNumInWindow[j][i];
                if (j < 3)
                    initialSmoothVal += flavorNumInWindow[j][i];
            }
            initialSmoothVal /= 3;

            //平滑系数
            double[][] smoothValue = new double[flavorNumInWindow.length + 1][2];
            smoothValue[0][0] = smoothValue[0][1] = initialSmoothVal;
            for (int j = 1; j < smoothValue.length; j++) {
                smoothValue[j][0] = factor * flavorData[j - 1] + (1 - factor) * smoothValue[j - 1][0];
                smoothValue[j][1] = factor * smoothValue[j][0] + (1 - factor) * smoothValue[j - 1][1];
            }

            //计算a、b
            double[] a = new double[smoothValue.length];
            double[] b = new double[smoothValue.length];
            double temp = factor / (1 - factor);
            for (int j = 0; j < smoothValue.length; j++) {
                a[j] = 2 * smoothValue[j][0] - smoothValue[j][1];
                b[j] = temp * (smoothValue[j][0] - smoothValue[j][1]);
            }

            //预测
            predictResult[i] = (int) Math.floor(a[a.length - 1] + b[b.length - 1]) - flavorNumInWindow[flavorNumInWindow.length - 1][i];
        }

        int[][] newFlavorNumByDay = new int[flavorNumByDay.length + 1][FLAVOR_TYPES];
        for (int i = 0; i < flavorNumByDay.length; i++) {
            for (int j = 0; j < flavorNumByDay[i].length; j++) {
                newFlavorNumByDay[i][j] = flavorNumByDay[i][j];
            }
        }
        for (int i = 0; i < FLAVOR_TYPES; i++) {
            newFlavorNumByDay[newFlavorNumByDay.length - 1][i] = predictResult[i];
        }
        return newFlavorNumByDay;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 预测训练集的下一天
     * @Date: 23:07 2018/4/19
     */
    private static int[][] predictNextDay(int[][] flavorNumInWindow, int[][] numByDay) {
        //训练模型
        double[][] weights = trainModel(flavorNumInWindow);

        //预测结果
        int[] predictResult = predictResult(flavorNumInWindow, weights);

        int[][] newFlavorNumByDay = new int[numByDay.length + 1][FLAVOR_TYPES];
        for (int i = 0; i < numByDay.length; i++) {
            for (int j = 0; j < numByDay[i].length; j++) {
                newFlavorNumByDay[i][j] = numByDay[i][j];
            }
        }
        for (int i = 0; i < FLAVOR_TYPES; i++) {
            newFlavorNumByDay[newFlavorNumByDay.length - 1][i] = predictResult[i];
        }
        return newFlavorNumByDay;
    }


    /**
     * @Author: Chy
     * @Param:
     * @Description: 统计各虚拟机在每个窗口中的数量
     * @Date: 18:56 2018/4/19
     */
    private static int[][] getDataInWindow(int trainDays, int predictDays, int[][] flavorNumByDay) {
        int windowNum = trainDays / predictDays; //滑窗数量
        int[][] flavorNumInWindow = new int[windowNum][FLAVOR_TYPES];
        for (int i = 0; i < windowNum; i++) {
            for (int j = 0; j < FLAVOR_TYPES; j++) {
                int sum = 0;
                for (int k = (windowNum - i - 1) * predictDays + 1; k < flavorNumByDay.length - i * predictDays; k++) {
                    sum += flavorNumByDay[k][j];
                }
                flavorNumInWindow[windowNum - i - 1][j] = sum;
            }
        }

        return flavorNumInWindow;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 箱形图去噪
     * @Date: 20:56 2018/3/31
     */
    private static void smoothByBox(int[][] flavorNumByDay) {

        for (int j = 0; j < FLAVOR_TYPES; j++) {
            //对每种flavor的历史数据进行升序排序
            int[] sortedArray = new int[flavorNumByDay.length];

            for (int i = 0; i < sortedArray.length; i++) {
                sortedArray[i] = flavorNumByDay[i][j];
            }
            Arrays.sort(sortedArray);

            List<Integer> noZeroList = new ArrayList<>();
            for (int i = 0; i < sortedArray.length; i++) {
                if (sortedArray[i] != 0) {
                    noZeroList.add(sortedArray[i]);
                }
            }
            if (noZeroList.size() < 4)
                continue;
            double Q1 = (noZeroList.size() + 1) * 1.0 / 4 - 1;  // 1/4分位点
            double Q2 = (noZeroList.size() + 1) * 2.0 / 4 - 1;  // 2/4分位点
            double Q3 = (noZeroList.size() + 1) * 3.0 / 4 - 1;  // 3/4分位点

            //获取每个分位点的值
            int intPart = (int) Q1;
            double decimalPart = Q1 - intPart;
            double q1Value = noZeroList.get(intPart) * (1 - decimalPart) + noZeroList.get(intPart + 1) * decimalPart;

            intPart = (int) Q2;
            decimalPart = Q2 - intPart;
            double q2Value = noZeroList.get(intPart) * (1 - decimalPart) + noZeroList.get(intPart + 1) * decimalPart;

            intPart = (int) Q3;
            decimalPart = Q3 - intPart;
            double q3Value = noZeroList.get(intPart) * (1 - decimalPart) + noZeroList.get(intPart + 1) * decimalPart;

            double IQR = q3Value - q1Value;
            double upper = q3Value + K * IQR;  //上内限
            double lower = q1Value - K * IQR;  //下内限
            for (int i = noZeroList.size() - 1; i >= 0; i--) {
                int value = noZeroList.get(i);
                if (value <= upper) {
                    upper = value;
                    break;
                }
            }
            for (int i = 0; i < noZeroList.size(); i++) {
                int value = noZeroList.get(i);
                if (value >= lower) {
                    lower = value;
                    break;
                }
            }

            //若值大于upper值，则将该值用upper值替代
            for (int i = 0; i < flavorNumByDay.length; i++) {
                if (flavorNumByDay[i][j] >= upper)
                    flavorNumByDay[i][j] = (int) Math.ceil(upper);
            }
        }

        for (int j = 0; j < FLAVOR_TYPES; j++) {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < flavorNumByDay.length; i++) {
                list.add(flavorNumByDay[i][j]);
            }
            int frequence = 0;
            int maxFrequenceValue = 0;
            for (Integer i : list) {
                if (i != 0) {
                    if (Collections.frequency(list, i) > maxFrequenceValue) {
                        frequence = Collections.frequency(list, i);
                        maxFrequenceValue = i;
                    }
                }
            }

            int noZeroCount = 0;
            int noZeroSum = 0;
            double average = 0.0;
            for (int i = 0; i < flavorNumByDay.length; i++) {
                if (flavorNumByDay[i][j] != 0) {
                    noZeroCount++;
                    noZeroSum += flavorNumByDay[i][j];
                }
            }
            if (noZeroCount == 0)
                average = 0;
            else
                average = Math.ceil(noZeroSum * 1.0 / noZeroCount);
//            System.out.println("flavor" + (j + 1) + "的非零平均值：" + average + " 非零数量：" + noZeroCount);
            for (int i = 0; i < flavorNumByDay.length; i++) {
                if (flavorNumByDay[i][j] > average * 3.5) {
                    flavorNumByDay[i][j] = maxFrequenceValue;
                }
            }
        }
    }

    /**
     * @Author: Chy
     * @Param: flavorNumByDay：虚拟机每天的数量
     * @Description: 平滑处理，用倒数第二大的值代替噪点 88+
     * @Date: 17:16 2018/3/19
     */

    private static void smooth(int[][] flavorNumByDay) {

        //第一步：若最高点大于次高点的1.7倍，则把最高点用次高点替代
        for (int j = 0; j < FLAVOR_TYPES; j++) {
            //对每种flavor的历史数据进行升序排序
            int[] sortedArray = new int[flavorNumByDay.length];
            for (int i = 0; i < sortedArray.length; i++) {
                sortedArray[i] = flavorNumByDay[i][j];
            }
            Arrays.sort(sortedArray);

            //若最大值大于1.7倍的次大值，则将最大值用次大值替代
            int maxVaule = sortedArray[sortedArray.length - 1];
            int secondMaxValue = sortedArray[sortedArray.length - 2];
            if (maxVaule >= 1.7 * secondMaxValue && secondMaxValue != 0) {
                for (int i = 0; i < flavorNumByDay.length; i++) {
                    if (flavorNumByDay[i][j] == maxVaule)
                        flavorNumByDay[i][j] = secondMaxValue;
                }
            }
        }

        //第二步：将大于非零平均数4倍的值也视为噪点，将其用非零众数替代
        for (int j = 0; j < FLAVOR_TYPES; j++) {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < flavorNumByDay.length; i++) {
                list.add(flavorNumByDay[i][j]);
            }
            int frequence = 0;
            int maxFrequenceValue = 0;
            for (Integer i : list) {
                if (i != 0) {
                    if (Collections.frequency(list, i) > maxFrequenceValue) {
                        frequence = Collections.frequency(list, i);
                        maxFrequenceValue = i;
                    }
                }
            }

            int noZeroCount = 0;
            int noZeroSum = 0;
            double average = 0.0;
            for (int i = 0; i < flavorNumByDay.length; i++) {
                if (flavorNumByDay[i][j] != 0) {
                    noZeroCount++;
                    noZeroSum += flavorNumByDay[i][j];
                }
            }
            if (noZeroCount == 0)
                average = 0;
            else
                average = Math.ceil(noZeroSum * 1.0 / noZeroCount);
//            System.out.println("flavor" + (j + 1) + "的非零平均值：" + average + " 非零数量：" + noZeroCount);
            for (int i = 0; i < flavorNumByDay.length; i++) {
                if (flavorNumByDay[i][j] > average * 4.0) {
                    flavorNumByDay[i][j] = maxFrequenceValue;
                }
            }
        }
    }

//    /**
//     * @Author: Chy
//     * @Param:
//     * @Description: 平滑处理，用非零众数代替噪点 85+
//     * @Date: 17:16 2018/3/19
//     */
//    private static void smooth(int[][] flavorNumByDay) {
//        int[][] smoothData = new int[flavorNumByDay.length][flavorNumByDay[0].length];
//        for (int i = 0; i < flavorNumByDay.length; i++){
//            for (int j = 0; j < flavorNumByDay[i].length; j++){
//                smoothData[i][j] = flavorNumByDay[i][j];
//            }
//        }
//
//        for (int j = 0; j < FLAVOR_TYPES; j++){
//
//            int[] sortedArray = new int[flavorNumByDay.length];
//            List<Integer> list = new ArrayList<>();
//            for (int i = 0; i < sortedArray.length; i++){
//                sortedArray[i] = flavorNumByDay[i][j];
//                list.add(flavorNumByDay[i][j]);
//            }
//            Arrays.sort(sortedArray);
//            int maxVaule = sortedArray[sortedArray.length - 1];
//            int secondMaxValue = sortedArray[sortedArray.length - 2];
//
//            int frequence = 0;
//            int maxFrequenceValue = 0;
//            for (Integer i : list){
//                if (i != 0){
//                    if (Collections.frequency(list, i) > maxFrequenceValue){
//                        frequence = Collections.frequency(list, i);
//                        maxFrequenceValue = i;
//                    }
//                }
//            }
//
//            if (maxVaule >= 2 * secondMaxValue){
//                for (int i = 0; i < smoothData.length; i++){
//                    if (flavorNumByDay[i][j] == maxVaule)
//                        flavorNumByDay[i][j] = maxFrequenceValue;
//                }
//            }
//        }
//    }

//    /**
//     * @Author: Chy
//     * @Param:
//     * @Description: 平滑处理，用非零数据的平均数替代噪点 84+
//     * @Date: 18:52 2018/3/19
//     */
//    private static void smooth(int[][] flavorNumByDay) {
//        int[][] smoothData = new int[flavorNumByDay.length][flavorNumByDay[0].length];
//        for (int i = 0; i < flavorNumByDay.length; i++){
//            for (int j = 0; j < flavorNumByDay[i].length; j++){
//                smoothData[i][j] = flavorNumByDay[i][j];
//            }
//        }
//
//        for (int j = 0; j < FLAVOR_TYPES; j++){
//
//            int[] sortedArray = new int[flavorNumByDay.length];
//
//            int noZeroCount = 0;
//            int noZeroSum = 0;
//            int average = 0;
//            for (int i = 0; i < sortedArray.length; i++){
//                sortedArray[i] = flavorNumByDay[i][j];
//                if(flavorNumByDay[i][j] != 0){
//                    noZeroCount++;
//                    noZeroSum += flavorNumByDay[i][j];
//                }
//            }
//            average = (int)Math.ceil(noZeroSum * 1.0 / noZeroCount);
//            Arrays.sort(sortedArray);
//
//            int maxVaule = sortedArray[sortedArray.length - 1];
//            int secondMaxValue = sortedArray[sortedArray.length - 2];
//
//            if (maxVaule > 2 * secondMaxValue){
//                for (int i = 0; i < smoothData.length; i++){
//                    if (flavorNumByDay[i][j] == maxVaule)
//                        flavorNumByDay[i][j] = average;
//                }
//            }
//        }
//    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 预测最终结果
     * @Date: 16:28 2018/3/17
     */
    private static int[] predictResult(int[][] flavorNumInWindow, double[][] weights) {

        //归一化
        double[][] scaling = featureScaling(flavorNumInWindow);

        int[] predictResult = new int[FLAVOR_TYPES];
        //预测结果
        for (int j = 0; j < FLAVOR_TYPES; j++) {
            int predictNum = 0;
            double temp = 0.0;
            for (int i = 1; i < scaling.length; i++) {
                temp += scaling[i][j] * weights[j][i];
            }
            temp = weights[j][0] + temp;
            predictNum = (int) Math.ceil(temp * (flavorNumInWindow[flavorNumInWindow.length - 1][j] - flavorNumInWindow[0][j] + flavorNumInWindow[0][j]));
            int minus = predictNum - flavorNumInWindow[flavorNumInWindow.length - 1][j];
            if (minus < 0)
                predictResult[j] = 0;
            else
                predictResult[j] = minus;

        }

        return predictResult;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 训练模型 线性模型
     * @Date: 12:23 2018/3/16
     */
    private static double[][] trainModel(int[][] flavorNumInWindow) {

        //归一化
        double[][] scaling = featureScaling(flavorNumInWindow);

        double[][] weights = new double[FLAVOR_TYPES][flavorNumInWindow.length];

        for (int j = 0; j < FLAVOR_TYPES; j++) {  //遍历18次，获取18种flavor的权重
            double[] weightsForEveflavor = new double[scaling.length]; //每种flavor的权重
            double predictValue = 0.0;

            int count = 0;
            while (count++ < ITERATORS) {
//                System.out.println("-----------第" + count + "次迭代---------");
                //计算预测值：线性回归
                predictValue = 0;
                for (int i = 0; i < scaling.length - 1; i++) {
                    predictValue += scaling[i][j] * weightsForEveflavor[i + 1];
                }
                predictValue += weightsForEveflavor[0];

                double diff = predictValue - scaling[scaling.length - 1][j];
                double costFunction = 0.5 * diff * diff;

//                if (count == ITERATORS - 1) {
//                    StringBuffer sb = new StringBuffer("j=" + j + " count=" + count + " diff:" + diff + " predictValue: " + predictValue +
//                            " costFunction:" + costFunction);
//                    System.out.println(sb.toString());
//                }

                //若损失函数已足够小，结束迭代
                if (Math.abs(costFunction) < MAX_COST) {
//                    StringBuffer sb = new StringBuffer("j=" + j + " count=" + count + " diff:" + diff + " predictValue: " + predictValue +
//                            " costFunction:" + costFunction);
//                    System.out.println(sb.toString());
                    break;
                }

                //梯度下降法更新每个参数
                for (int k = 0; k < weightsForEveflavor.length; k++) {
                    if (k == 0) {
                        weightsForEveflavor[k] = weightsForEveflavor[k] - LEARNING_RATE * diff;
                    } else if (k == weightsForEveflavor.length - 4) {
                        weightsForEveflavor[k] = weightsForEveflavor[k] - LEARNING_RATE * diff * scaling[k][j] / k / k;
                    } else if (k == weightsForEveflavor.length - 3) {
                        weightsForEveflavor[k] = weightsForEveflavor[k] - LEARNING_RATE * diff * scaling[k][j] / k / k;
                    } else if (k == weightsForEveflavor.length - 2) {
                        weightsForEveflavor[k] = weightsForEveflavor[k] - LEARNING_RATE * diff * scaling[k][j] / k;
                    } else {
                        weightsForEveflavor[k] = weightsForEveflavor[k] - LEARNING_RATE * diff * scaling[k][j];
                    }
//                    System.out.println(weightsForEveflavor[k]);
                }
            }
            for (int m = 0; m < weightsForEveflavor.length; m++) {
                weights[j][m] = weightsForEveflavor[m];
            }

        }
        return weights;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 计算一个数的指定次方
     * @Date: 20:41 2018/3/18
     */
    private static double multiplication(int count, int value) {
        double result = 1.0;
        for (int i = 0; i < count; i++) {
            result *= value;
        }
        return result;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 归一化
     * @Date: 19:35 2018/3/17
     */
    public static double[][] featureScaling(int[][] flavorNumInWindow) {
        double[][] scaling = new double[flavorNumInWindow.length][FLAVOR_TYPES];

        for (int j = 0; j < FLAVOR_TYPES; j++) {
            int min = flavorNumInWindow[0][j];
            int max = flavorNumInWindow[flavorNumInWindow.length - 1][j];

            for (int i = 0; i < flavorNumInWindow.length; i++) {
                if (min == max && min == 0) {
                    scaling[i][j] = 0;
                } else if (min == max && min != 0) {
                    scaling[i][j] = 1;
                } else {
                    scaling[i][j] = (flavorNumInWindow[i][j] - min) * 1.0 / (max - min);
                }
            }
        }

        return scaling;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 获取输入文件中虚拟机的名称
     * @Date: 13:55 2018/3/10
     */
    private static List<String> getNameListFromInput(String[] inputContent) {
        int serverType = Integer.parseInt(inputContent[0]);  //服务器种类数
        int flavorType = Integer.parseInt(inputContent[2 + serverType]);   //虚拟机种类数

        List<String> nameList = new ArrayList<>();
        for (int i = 3 + serverType; i < 3 + serverType + flavorType; i++) {
            nameList.add(inputContent[i].split(" ")[0]);
        }
        return nameList;
    }
}
